package javaNIO1.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.DatagramChannel;
import java.util.*;

/**
 * Created with Intel IDEA.
 * 使用DatagramChannel 发送请求到多个地址上的服务时间
 *
 * @Auther: gjt
 * @Date: 2024/7/21 10:54
 */
public class TimeClient {

    private static final int PORT = 37;
    protected int port = PORT;
    protected List remoteHosts;
    protected DatagramChannel datagramChannel;

    private static  final long DIFF_1900 = 2208988800L;

    public TimeClient(String[] args) throws IOException {

        if (args.length == 0) {
            System.out.println("请输入参数");
        } else {
            parseArgs(args);
            this.datagramChannel = DatagramChannel.open();
        }
    }


    // 接收包
    protected InetSocketAddress receivePacket(DatagramChannel datagramChannel, ByteBuffer buffer) throws IOException {
        buffer.clear();

        // 接受无符号的32 位 并且字节顺序是大端的
        return (InetSocketAddress) datagramChannel.receive(buffer);
    }

    // 发送请求 就是把 网络地址类和直接字节缓冲区  放入到DatagramChannel中并发送
    protected void sendRequests() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(1);
        for (Object remoteHost : remoteHosts) {
            // SocketAddress 是一个接口  InetSocketAddress 是SocketAddress 的唯一子类
            InetSocketAddress next = (InetSocketAddress) remoteHost;
            System.out.println("Requesting timme fromm " + next.getHostName() + ":" + next.getPort());

            // 使缓冲区空的状态
            buffer.clear().flip();
            // 发送请求
            datagramChannel.send(buffer, next);
        }
    }

    //，接收响应
    protected void getReplies() throws IOException {
        // 分配一个支持 long 值的缓冲区
        ByteBuffer longBuffer = ByteBuffer.allocateDirect(8);

        // 使用大端字节顺序
        longBuffer.order(ByteOrder.BIG_ENDIAN);

        // 保证缓冲区中都是0
        longBuffer.putLong(0,0);

        //  跳过前4个字节 问题
        longBuffer.position(4);

        // 分割缓冲区 生成的子缓冲区会继承只读和直接属性
        ByteBuffer sliceBuffer = longBuffer.slice();
        int size = remoteHosts.size();

        int replies = 0;
        System.out.println("");
        System.out.println("Waiting for replies ...");
        while(true){

            InetSocketAddress sa = receivePacket(datagramChannel,sliceBuffer);
            sliceBuffer.clear();

            replies++;
            printTime(longBuffer.getLong(0), sa);
            if(replies == size){
                System.out.println("All packets answers");
                break;
            }

            System.out.println("Received " + replies + " of" + size + "replies" );
        }

    }


    protected void printTime(long seconds, InetSocketAddress sa) {
        long local = System.currentTimeMillis() / 1000;
        long remote = seconds - DIFF_1900;
        Date remoteDate = new Date(remote * 1000);
        Date localDate = new Date(local * 1000);

        // 相差秒数
        long skew = remote - local;
        System.out.println("Reply from " + sa.getHostName() + ":" + sa.getPort());

        System.out.println("there :" + remoteDate);
        System.out.println("here :" + localDate);
        System.out.println("skew :" );

        if(skew == 0){
            System.out.println("none");
        } else if (skew > 0) {
            System.out.println(skew + " seconds ahead");
        }else {
            System.out.println((-skew) + "seconds behead");
        }
    }


    protected void parseArgs(String[] args) {
        remoteHosts = new LinkedList();
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            if (args[i].equals("-p")) {
                this.port = Integer.parseInt(args[++i]);
                continue;
            }
            InetSocketAddress address = new InetSocketAddress(arg, port);
            if(address.getAddress() == null){
                System.out.println("Can't resolve address:" + arg);
                continue;
            }
            remoteHosts.add(address);
        }
    }

    public static void main(String[] args) throws IOException {
        TimeClient timeClient = new TimeClient(args);
        timeClient.sendRequests();
        timeClient.getReplies();
    }
}
